import { Injectable } from '@nestjs/common';
import * as csv from 'csv-parser';
import * as fs from 'fs';
import * as readline from 'readline';

@Injectable()
export class NetworkService {
  private csvsDir: string;
  private devices: any[];
  private topology: any;
  private fibs: any;

  constructor() {
    this.csvsDir = '/tmp/flashlight/csvs';
    this.loadDevices();
    this._getTopology();
    this.loadFib();
  }

  private loadFib() {
    const ri = readline.createInterface({
      crlfDelay: Infinity,
      input: fs.createReadStream(
        '/mnt/d/exp-data/ibdiagnet2/ibdiagnet2/ibdiagnet2.far',
      ),
    });
    const fibs = {};
    let groups = {};
    let currSw = '';
    ri.on('line', (line) => {
      const items = line.split(/\s+/);
      if (items[0] === 'Switch') {
        currSw = items[1].slice(2);
        fibs[currSw] = {};
        groups = {};
      } else if (items[0].startsWith('0x')) {
        const match = Number.parseInt(items[0]);
        let action = groups[items[3]];
        if (items[2] === 'Static') {
          action = [Number.parseInt(items[1])];
        }
        fibs[currSw][match] = action;
      } else if (!Number.isNaN(Number.parseInt(items[0]))) {
        groups[items[0]] =
          items.length === 3 ? [] : items[3].split(',').slice(0, -1);
      }
    });
    ri.on('close', () => {
      // fs.writeFileSync('output.json', JSON.stringify(fibs, null, 4));
      // console.log(fibs);
      console.log(Object.keys(fibs).length);
    });
    this.fibs = fibs;
  }

  private _getTopology() {
    const ri = readline.createInterface({
      input: fs.createReadStream(
        '/mnt/d/exp-data/ibdiagnet2/ibdiagnet2/ibdiagnet2.ibnetdiscover',
      ),
    });
    const topo: { nodes: { [key: string]: { id: string; name: string; type: string } }; edges: { source: string; target: string; p1: string; p2: string }[] } = {
      nodes: {},
      edges: [],
    };
    let guid = '';
    ri.on('line', (line) => {
      if (line.startsWith('Switch') || line.startsWith('Ca')) {
        // Switch  65 "S-fc6a1c03009b0180"      # "SHLG-PSC-ZS3F1-POD1-JQ1-CS-HJ-ZONE2-1/1" base port 0 lid 2015 lmc 0
        const match = /.*-(.*)".*"(.*)"/.exec(line);
        if (!match) return;
        const [_, _guid, name] = match;
        // const [_, _guid, name, lid] = /.*-(.*)".*"(.*)".*lid (.*) lmc/.exec(line);
        // const [_1, _2, _guid, _3, name, _4, _5, _6, _7, lid] =
        // line.split(/[\s"S-]+/);
        if (name.startsWith('Mellanox Technologies')) {
          // ignore virtual port
          return;
        }
        guid = _guid;
        const node = {
          id: guid,
          name,
          type: line.startsWith('Ca') ? 'ca' : 'switch',
        };
        topo.nodes[guid] = node;
      }
      if (line.startsWith('[')) {
        const match = /\[(.*?)\].*-(.*)"\[(.*?)\]/.exec(line);
        if (!match) return;
        const [_, p1, target, p2] = match;
        if (p1 === '65' || p2 === '65') {
          // ignore the virtual ports of switches
          return;
        }
        const edge = {
          source: guid,
          target,
          p1,
          p2,
        };
        topo.edges.push(edge);
      }
    });
    this.topology = topo;
  }

  private async loadDevices() {
    this.devices = await new Promise<any[]>((resolve, reject) => {
      const results: any[] = [];
      fs.createReadStream(`${this.csvsDir}/NODES.csv`)
        .pipe(csv())
        .on('data', (row: any) => {
          results.push(row);
        })
        .on('end', () => {
          console.log('Devices info loaded');
          resolve(results);
        })
        .on('error', (error) => {
          reject(error);
        });
    });
  }

  getDevices() {
    return this.devices;
  }

  getDevice(id: string) {
    return this.devices.find((d) => d['NodeGUID'] === `0x${id}`);
  }

  getTopology() {
    return this.topology;
  }

  getFib(id: string) {
    return this.fibs[id];
  }
}
